home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-20 / nrd33.zip / ASYNC.INC < prev    next >
Text File  |  1990-07-06  |  18KB  |  495 lines

  1. Unit Async;
  2.  
  3. Interface
  4.  
  5. Uses DOS;
  6. {--------------------------------------------------------------}
  7. {                        ASYNC.INC                             }
  8. {                                                              }
  9. {  Async Communication Routines                                }
  10. {  by Michael Quinlan                                          }
  11. {  with a bug fixed by Scott Herr                              }
  12. {  with Async_ISR update to 4.0 by N. Arley Dealey substituted }
  13. {                               by Keith Hawes                 }
  14. {  made PCjr-compatible by W. M. Miller                        }
  15. {  Highly dependent on the IBM PC and PC DOS 2.0               }
  16. {                                                              }
  17. {  based on the DUMBTERM program by CJ Dunford                 }
  18. {  in the January 1984                                         }
  19. {  issue of PC Tech Journal.                                   }
  20. {                                                              }
  21. {  Entry points:                                               }
  22. {--------------------------------------------------------------}
  23.  
  24. Procedure Async_Init;
  25. {--------------------------------------------------------------}
  26. {      Performs initialization.                                }
  27. {                                                              }
  28. {--------------------------------------------------------------}
  29.  
  30. function Async_Open(ComPort       : Word;
  31.                     BaudRate      : Word;
  32.                     Parity        : Char;
  33.                     WordSize      : Word;
  34.                     StopBits      : Word) : Boolean;
  35. {--------------------------------------------------------------}
  36. {   Sets up interrupt vector, initialize the COM port for      }
  37. {   processing, sets pointers to the buffer.  Returns FALSE    }
  38. {   if COM                                                     }
  39. {      port not installed.                                     }
  40. {--------------------------------------------------------------}
  41.  
  42. Function Async_Buffer_Check(var C : Char) : Boolean;
  43. {--------------------------------------------------------------}
  44. {      If a character is available, returns TRUE and moves the }
  45. {        character from the buffer to the parameter            }
  46. {      Otherwise, returns FALSE                                }
  47. {--------------------------------------------------------------}
  48.  
  49. Procedure Async_Send(C : Char);
  50. {--------------------------------------------------------------}
  51. {      Transmits the character.                                }
  52. {--------------------------------------------------------------}
  53.  
  54. Procedure Async_Send_String(S : string);
  55. {--------------------------------------------------------------}
  56. {      Calls Async_Send to send each character of S.           }
  57. {--------------------------------------------------------------}
  58.  
  59. Procedure Async_Close;
  60. {--------------------------------------------------------------}
  61. {    Turn off the COM port interrupts.                         }
  62. {    will see some really strange errors and have to re-boot.  }
  63. {--------------------------------------------------------------}
  64.  
  65. procedure Async_Change(BaudRate      : Word;
  66.                        Parity        : Char;
  67.                        WordSize      : Word;
  68.                        StopBits      : Word);
  69. {--------------------------------------------------------------}
  70. { change communication parameters "on the fly"                 }
  71. { you cannot use the BIOS routines because they drop DTR       }
  72. {--------------------------------------------------------------}
  73.  
  74. var
  75.   Async_Buffer_Overflow : Boolean;  { True if buffer overflow has happened }
  76.   Async_Buffer_Used     : Word;
  77.   Async_MaxBufferUsed   : Word;
  78.  
  79. Implementation
  80. { global declarations }
  81.  
  82. const
  83.   UART_THR = $00;
  84.       { offset from base of UART Registers for IBM PC }
  85.   UART_RBR = $00;
  86.   UART_IER = $01;
  87.   UART_IIR = $02;
  88.   UART_LCR = $03;
  89.   UART_MCR = $04;
  90.   UART_LSR = $05;
  91.   UART_MSR = $06;
  92.  
  93.   I8088_IMR = $21;
  94.        { port address of the Interrupt Mask Register }
  95.  
  96. const
  97.   Async_Buffer_Max       = 4095;
  98. var
  99.   Async_Interrupt_Save   : pointer;
  100.   Async_ExitProc_Save    : pointer;
  101.   Async_Buffer           : Array[0..Async_Buffer_Max] of char;
  102.   Async_Open_Flag        : Boolean;
  103.   Async_Port             : Word; { current Open port number (1 or 2)    }
  104.   Async_Base             : Word; { base for current open port           }
  105.   Async_Irq              : Word; { irq for current open port            }
  106.  
  107.     { Async_Buffer is empty if Head = Tail }
  108.  
  109.   Async_Buffer_Head     : Word;    { Locn in Async_Buffer to put next char }
  110.   Async_Buffer_Tail     : Word;    { Locn in Async_Buffer to get next char }
  111.   Async_Buffer_NewTail  : Word;
  112.  
  113.   Async_BIOS_Port_Table : Array[1..2] of Word absolute $40:0;
  114.     { This table is initialized by BIOS equipment determination
  115.     code at boot time to contain the base addresses for the
  116.     installed async adapters.  A value of 0 means "not in-
  117.     stalled." }
  118.  
  119. const
  120.   Async_Num_Bauds = 8;
  121.   Async_Baud_Table : array [1..Async_Num_Bauds] of record
  122.                                          Baud, Bits : Word
  123.                                       end
  124.                    = ((Baud:110;  Bits:$00),
  125.                       (Baud:150;  Bits:$20),
  126.                       (Baud:300;  Bits:$40),
  127.                       (Baud:600;  Bits:$60),
  128.                       (Baud:1200; Bits:$80),
  129.                       (Baud:2400; Bits:$A0),
  130.                       (Baud:4800; Bits:$C0),
  131.                       (Baud:9600; Bits:$E0));
  132.  
  133. procedure BIOS_RS232_Init(ComPort, ComParm : Word);
  134. { Issue Interrupt $14 to initialize the UART   }
  135. { Format of ComParm:  (From IBM Tech. Ref.)    }
  136. {                                              }
  137. { 7     6     5     4     3     2      1     0 }
  138. { --Baud Rate--     -Parity   StopBit  Word Len}
  139. {  000 =  110       x0 = None   0 = 1  10 = 7  }
  140. {  001 =  150       01 = Odd    1 = 2  11 = 8  }
  141. {  010 =  300       11 = Even                  }
  142. {  011 =  600                                  }
  143. {  100 = 1200                                  }
  144. {  101 = 2400                                  }
  145. {  110 = 4800                                  }
  146. {  111 = 9600                                  }
  147. {                                              }
  148.  
  149. var
  150.   Regs : registers;
  151. begin
  152.   with Regs do
  153.     begin
  154.       ax := ComParm and $00FF;  { AH=0; AL=ComParm }
  155.       dx := ComPort;
  156.       Intr($14, Regs)
  157.     end;
  158. end; { BIOS_RS232_Init }
  159.  
  160. {---------------------------------------------------------------------------}
  161. {                      ISR - Interrupt Service Routine                      }
  162. {---------------------------------------------------------------------------}
  163.  
  164. PROCEDURE Async_ISR ; INTERRUPT ;
  165. { Interrupt Service Routine }
  166. { Invoked when the USART has received a byte of data from the comm line     }
  167. { re-written 9/10/84 in machine language ; original source left as comments }
  168. { re-written 1987    to work under Turbo Pascal Version 4.0                 }
  169.  
  170. BEGIN { ISR }
  171.   inline(
  172.     $FB/                                { STI }
  173.  
  174.     { get the incoming character }
  175.     { Async_Buffer[Async_Buffer_Head] :=
  176.                                 CHR( port[Async_Base + DG1_USART_Data] ) ;  }
  177.     $8B/$16/Async_Base/                       { MOV DX,Base }
  178.     $EC/                                      { IN AL,DX }
  179.     $8B/$1E/Async_Buffer_Head/                { MOV BX,BufferHead }
  180.     $88/$87/Async_Buffer/                     { MOV Buffer[BX],AL }
  181.  
  182.     { Async_Buffer_NewHead := SUCC( Async_Buffer_Head ) ;           }
  183.     $43/                                      { INC BX }
  184.  
  185.     { IF Async_Buffer_NewHead > Async_Buffer_Max
  186.             THEN Async_Buffer_NewHead := 0 ; }
  187.     $81/$FB/Async_Buffer_Max/                 { CMP BX,BufferMax }
  188.     $7E/$02/                                  { JLE L001 }
  189.     $33/$DB/                                  { XOR BX,BX }
  190.  
  191.     { IF Async_Buffer_NewHead = Async_Buffer_Tail THEN Overflow := TRUE }
  192.     {L001:}
  193.     $3B/$1E/Async_Buffer_Tail/                { CMP BX,Async_Buffer_Tail }
  194.     $75/$08/                                  { JNE L002 }
  195.     $C6/$06/Async_Buffer_Overflow/$01/        { MOV Overflow,1 }
  196.     $90/                                      { NOP generated by assembler }
  197.     $EB/$16/                                  { JMP SHORT L003 }
  198.     { ELSE BEGIN                                                    }
  199.     {   Async_Buffer_Head := Async_Buffer_NewHead ;                 }
  200.     {   Async_Buffer_Used  := SUCC( Async_Buffer_Used ) ;           }
  201.     {   IF Async_Buffer_Used > Async_MaxBufferUsed THEN             }
  202.     {     Async_MaxBufferUsed := Async_BufferUsed                   }
  203.     {   END ;                                                       }
  204.     {L002:}
  205.     $89/$1E/Async_Buffer_Head/                { MOV BufferHead,BX }
  206.       $FF/$06/Async_Buffer_Used/              { INC Async_BufferUsed }
  207.       $8B/$1E/Async_Buffer_Used/              { MOV BX,Async_BufferUsed }
  208.       $3B/$1E/Async_MaxBufferUsed/            { CMP BX,Async_MaxBufferUsed }
  209.       $7E/$04/                                { JLE L003 }
  210.       $89/$1E/Async_MaxBufferUsed/            { MOV Async_MaxBufferUsed,BX }
  211.     {L003:}
  212.  
  213.     $FA/                                      { CLI }
  214.  
  215.     { issue non-specific EOI }
  216.     { port[$20] := $20 ;                                                }
  217.     $B0/$20/                                  { MOV AL,20h }
  218.     $E6/$20                                   { OUT 20h,AL }
  219.     )
  220.   END { Async_ISR } ;
  221.  
  222.  
  223. procedure Async_Init;
  224. { initialize variables }
  225. begin
  226.   Async_Open_Flag := FALSE;
  227.   Async_Buffer_Overflow := FALSE;
  228.   Async_Buffer_Used := 0;
  229.   Async_MaxBufferUsed := 0;
  230. end; { Async_Init }
  231.  
  232. procedure Async_Close;
  233. { reset the interrupt system when UART interrupts
  234.   no longer needed }
  235. var
  236.   i, m : Word;
  237. begin
  238.   if Async_Open_Flag then
  239.     begin
  240.  
  241.       { disable the IRQ on the 8259 }
  242.       Inline($FA);                              { disable interrupts }
  243.       i := Port[I8088_IMR];        { get the interrupt mask register }
  244.       m := 1 shl Async_Irq;         { set mask to turn off interrupt }
  245.       Port[I8088_IMR] := i or m;
  246.  
  247.       { disable the 8250 data ready interrupt }
  248.       Port[UART_IER + Async_Base] := 0;
  249.  
  250.       { disable OUT2 on the 8250 }
  251.       Port[UART_MCR + Async_Base] := 0;
  252.       Inline($FB);         { enable interrupts }
  253.  
  254.       { re-initialize our data areas so
  255.         we know the port is closed }
  256.       Async_Open_Flag := FALSE;
  257.  
  258. {Version 4 support by Keith Hawes next 2 lines}
  259.       SetIntVec( Async_IRQ + 8, Async_Interrupt_Save ); {restore old interupt}
  260.       ExitProc := Async_ExitProc_Save;                {restore ExirProc chain}
  261.     end
  262. end; { Async_Close }
  263.  
  264. function Async_Open(ComPort       : Word;
  265.                     BaudRate      : Word;
  266.                     Parity        : Char;
  267.                     WordSize      : Word;
  268.                     StopBits      : Word) : Boolean;
  269. { open a communications port }
  270. var
  271.   ComParm : Word;
  272.   i, m : Word;
  273. begin
  274.   if Async_Open_Flag then Async_Close;
  275.  
  276.   if (ComPort = 2) and (Async_BIOS_Port_Table[2] <> 0) then
  277.     Async_Port := 2
  278.   else
  279.     Async_Port := 1;  { default to COM1 }
  280.   Async_Base := Async_BIOS_Port_Table[Async_Port];
  281.   Async_Irq := Hi(Async_Base) + 1;
  282.  
  283.   if (Port[UART_IIR + Async_Base] and $00F8) <> 0 then
  284.     Async_Open := FALSE
  285.   else
  286.     begin
  287.       Async_Buffer_Head := 0;
  288.       Async_Buffer_Tail := 0;
  289.       Async_Buffer_Overflow := FALSE;
  290.  
  291.   { Build the ComParm for RS232_Init }
  292.   { See Technical Reference Manual for description }
  293.  
  294.       ComParm := $0000;
  295.  
  296.   { Set up the bits for the baud rate }
  297.       i := 0;
  298.       repeat
  299.         i := i + 1
  300.       until (Async_Baud_Table[i].Baud = BaudRate)
  301.               or (i = Async_Num_Bauds);
  302.       ComParm := ComParm or Async_Baud_Table[i].Bits;
  303.  
  304.       if Parity in ['E', 'e'] then ComParm := ComParm or $0018
  305.       else if Parity in ['O', 'o'] then
  306.            ComParm := ComParm or $0008
  307.       else ComParm := ComParm or $0000;  { default to No parity }
  308.       if WordSize = 7 then ComParm := ComParm or $0002
  309.       else ComParm := ComParm or $0003;  { default to 8 data bits }
  310.  
  311.       if StopBits = 2 then ComParm := ComParm or $0004
  312.       else ComParm := ComParm or $0000;  { default to 1 stop bit }
  313.  
  314.       { use the BIOS COM port initialization routine
  315.         to save typing the code }
  316.  
  317.       BIOS_RS232_Init(Async_Port - 1, ComParm);
  318.       GetIntVec( Async_Irq + 8, Async_Interrupt_Save ); {Version 4 support KH}
  319.       Async_ExitProc_Save := ExitProc;       {Version 4 support by Keith Hawes}
  320.       ExitProc := @Async_Close;              {Version 4 support by Keith Hawes}
  321.       SetIntVec( Async_Irq + 8, @Async_Isr );{Version 4 support by Keith Hawes}
  322.  
  323. { read the RBR and reset any possible pending error conditions
  324.   first turn off the Divisor Access Latch Bit to allow
  325.   access to RBR, etc. }
  326.  
  327.       Inline($FA);  { disable interrupts }
  328.  
  329.       Port[UART_LCR + Async_Base] :=
  330.               Port[UART_LCR + Async_Base] and $7F;
  331.       { read the Line Status Register to reset any
  332.         errors it indicates }
  333.       i := Port[UART_LSR + Async_Base];
  334.       { read the Receiver Buffer Register in case
  335.         it contains a character }
  336.       i := Port[UART_RBR + Async_Base];
  337.  
  338.       { enable the irq on the 8259 controller }
  339.       i := Port[I8088_IMR];  { get the interrupt mask register }
  340.       m := (1 shl Async_Irq) xor $00FF;
  341.       Port[I8088_IMR] := i and m;
  342.  
  343.       { enable the data ready interrupt on the 8250 }
  344.       Port[UART_IER + Async_Base] := $01;
  345.       { enable data ready interrupt }
  346.  
  347.       { enable OUT2 on 8250 }
  348.       i := Port[UART_MCR + Async_Base];
  349.       Port[UART_MCR + Async_Base] := i or $08;
  350.       Inline($FB); { enable interrupts }
  351.       Async_Open_Flag := TRUE;  { bug fix by Scott Herr }
  352.       Async_Open := TRUE
  353.     end;
  354. end; { Async_Open }
  355.  
  356. function Async_Buffer_Check(var C : Char) : Boolean;
  357. { see if a character has been received; return it if yes }
  358. begin
  359.   if Async_Buffer_Head = Async_Buffer_Tail then
  360.     Async_Buffer_Check := FALSE
  361.   else
  362.     begin
  363.       C := Async_Buffer[Async_Buffer_Tail];
  364.       Async_Buffer_Tail := Async_Buffer_Tail + 1;
  365.       if Async_Buffer_Tail > Async_Buffer_Max then
  366.         Async_Buffer_Tail := 0;
  367.       Async_Buffer_Used := Async_Buffer_Used - 1;
  368.       Async_Buffer_Check := TRUE
  369.     end
  370. end; { Async_Buffer_Check }
  371.  
  372. procedure Async_Send(C : Char);
  373. { transmit a character }
  374. var
  375.   i, m, counter : Word;
  376. begin
  377.   Port[UART_MCR + Async_Base] := $0B; { turn on OUT2, DTR, and RTS }
  378.  
  379.   { wait for CTS }
  380.   counter := MaxInt;
  381.   while (counter <> 0) and
  382.         ((Port[UART_MSR + Async_Base] and $10) = 0) do
  383.     counter := counter - 1;
  384.  
  385.   { wait for Transmit Hold Register Empty (THRE) }
  386.   if counter <> 0 then counter := MaxInt;
  387.   while (counter <> 0) and
  388.         ((Port[UART_LSR + Async_Base] and $20) = 0) do
  389.     counter := counter - 1;
  390.   if counter <> 0 then
  391.     begin
  392.       { send the character }
  393.       Inline($FA); { disable interrupts }
  394.       Port[UART_THR + Async_Base] := Ord(C);
  395.       Inline($FB) { enable interrupts }
  396.     end
  397.   else
  398.     writeln('<<<TIMEOUT>>>');
  399. end; { Async_Send }
  400.  
  401. procedure Async_Send_String(S : String);
  402. { transmit a string }
  403. var
  404.   i : Word;
  405. begin
  406.   for i := 1 to length(S) do
  407.     Async_Send(S[i])
  408. end; { Async_Send_String }
  409.  
  410. procedure Async_Change(BaudRate      : Word;
  411.                        Parity        : Char;
  412.                        WordSize      : Word;
  413.                        StopBits      : Word);
  414. { change communication parameters "on the fly" }
  415. { you cannot use the BIOS routines because they drop DTR }
  416.  
  417. const num_bauds = 15;
  418.     divisor_table : array [1..num_bauds] of record
  419.                                             baud, divisor : Word
  420.                                           end
  421.        = ((baud:50;  divisor:2304),
  422.           (baud:75;  divisor:1536),
  423.           (baud:110; divisor:1047),
  424.           (baud:134; divisor:857),
  425.           (baud:150; divisor:768),
  426.           (baud:300; divisor:384),
  427.           (baud:600; divisor:192),
  428.           (baud:1200; divisor:96),
  429.           (baud:1800; divisor:64),
  430.           (baud:2000; divisor:58),
  431.           (baud:2400; divisor:48),
  432.           (baud:3600; divisor:32),
  433.           (baud:4800; divisor:24),
  434.           (baud:7200; divisor:16),
  435.           (baud:9600; divisor:12));
  436.  
  437. var i : Word;
  438.     dv  : Word;
  439.     lcr : Word;
  440. begin
  441.  
  442.   { Build the Line Control Register and find
  443.     the divisor (for the baud rate) }
  444.  
  445.   { Set up the divisor for the baud rate }
  446.   i := 0;
  447.   repeat
  448.     i := i + 1
  449.   until (Divisor_Table[i].Baud = BaudRate) or (i = Num_Bauds);
  450.   dv  := Divisor_Table[i].divisor;
  451.  
  452.   lcr := 0;
  453.   case Parity of
  454.     'E' : lcr := lcr or $18;  { even parity }
  455.     'O' : lcr := lcr or $08;  { odd parity }
  456.     'N' : lcr := lcr or $00;  { no parity }
  457.     'M' : lcr := lcr or $28;  { Mark parity }
  458.     'S' : lcr := lcr or $38;  { Space parity }
  459.   else
  460.     lcr := lcr or $00;  { default to no parity }
  461.   end;
  462.  
  463.   case WordSize of
  464.     5 : lcr := lcr or $00;
  465.     6 : lcr := lcr or $01;
  466.     7 : lcr := lcr or $02;
  467.     8 : lcr := lcr or $03;
  468.   else
  469.     lcr := lcr or $03;  { default to 8 data bits }
  470.   end;
  471.  
  472.   if StopBits = 2 then lcr := lcr or $04
  473.   else lcr := lcr or $00;  { default to 1 stop bit }
  474.  
  475.   lcr := lcr and $7F;   { make certain the DLAB is off }
  476.  
  477.   Inline($FA);  { disable interrupts }
  478.  
  479.   { turn on DLAB to access the divisor }
  480.   Port[UART_LCR + Async_Base] := Port[UART_LCR + Async_Base] or $80;
  481.  
  482.   { set the divisor }
  483.   Port[Async_Base] := Lo(dv);
  484.   Port[Async_Base + 1] := Hi(dv);
  485.  
  486.   { turn off the DLAB and set the new comm. parameters }
  487.   Port[UART_LCR + Async_Base] := lcr;
  488.  
  489.   Inline($FB);  { enable interrupts }
  490.  
  491. end; { Async_Change }
  492. end.
  493.  
  494.  
  495.